SpatialInterpolation.f90 Source File

Interpolate sparse point measurements to regular grid



Source Code

!! Interpolate sparse point measurements to regular grid  
!|author:  <a href="mailto:giovanni.ravazzani@polimi.it">Giovanni Ravazzani</a>
! license: <a href="http://www.gnu.org/licenses/">GPL</a>
!    
!### History
!
! current version  1.5 - 18th December 2018  
!
! | version  |  date       |  comment |
! |----------|-------------|----------|
! | 1.0      | 03/Jun/2011 | Original code |
! | 1.1      | 01/apr/2013 | added Barnes Objective Analysis interpolation |
! | 1.2      | 20/Feb/2016 | added Kriging interpolation |
! | 1.3      | 09/Mar/2016 | WindMicromet added |
! | 1.4      | 01/Mar/2018 | WindGonzalezLongatt added |
! | 1.5      | 18/Dec/2018 | Kriging was moved to a specific module |
!
!### License  
! license: GNU GPL <http://www.gnu.org/licenses/>
!
!### Module Description 
!   set of generic routines to convert sparse point measurements to regular grid  
!   Methods implemented:
!
! * Inverse Distance Weighted
! * Nearest Neighbor (Thiessen polygons)
! * Barnes Objective Analysis
! * Kriging with automatic fitting of linear semivariogram (moved to a specific module with several semivariogram models)
! * Wind with topographic correction Micromet
! * Wind with orographic correction (Gonzales-Longatt etal., 2015)
!
! References:
!
!  González-Longatt, F., Medina, H., Serrano González, J., Spatial interpolation 
!     and orographic correction to estimate wind energy resource in Venezuela.
!     Renewable and Sustainable Energy Reviews, 48, 1-16, 2015.
!
MODULE SpatialInterpolation


USE DataTypeSizes, ONLY:&
  !Imported parameters:
  float, double, short

USE LogLib, ONLY: &
  !Imported routines:
  Catch

USE ObservationalNetworks, ONLY: &
  !Imported definitions:
  ObservationalNetwork, &
  !Imported routines:
  ActualObservations, CopyNetwork, &
  DestroyNetwork

USE GridLib, ONLY: &
  !Imported definitions:
  grid_real, grid_integer, &
  !Imported routines:
  NewGrid, GridDestroy, &
  !Imported operators:
  ASSIGNMENT( = )

USE GridOperations, ONLY: &
  !Imported routines:
  GetXY, CellArea, CellIsValid


USE GridStatistics, ONLY: &
  !Imported routines:
  CountCells
  
USE GeoLib, ONLY: &
  !Imported variables:
  point1, point2, &
  !Imported routines:
  Distance, &
  !Imported operators:
  ASSIGNMENT( = )


IMPLICIT NONE

! Global (i.e. public) Declarations: 

! Global Procedures:

PUBLIC :: IDW
PUBLIC :: NearestNeighbor
PUBLIC :: BarnesOI
PUBLIC :: WindMicromet
PUBLIC :: WindGonzalezLongatt
!PUBLIC :: Zonal TODO


!Private procedures
PRIVATE :: Sort
PRIVATE :: Inverse

!=======         
    CONTAINS
!======= 
! Define procedures contained in this module. 

!==============================================================================
!| Description:
!   Inverse Distance Weighted interpolation. Accept as optional argument
!   the power parameter and the number of near observations to 
!   included in interpolation. Can use Shepard's method (Shepard 1968) 
!   or Franke & Nielson, 1980
!
! References:
!
!   Shepard, D. (1968) A two-dimensional interpolation function for 
!   irregularly-spaced data, Proc. 23rd National Conference ACM, ACM, 517-524. 
!
!   Franke, R. and G. Nielson (1980), Smooth interpolation of large sets
!   of scattered data. International Journal of Numerical Methods 
!   in Engineering, 15, 1691-1704.
SUBROUTINE IDW &
!
(network, grid, method, power, near)


IMPLICIT NONE

!Arguments with intent (in):
TYPE (ObservationalNetwork), INTENT(IN) :: network
INTEGER (KIND = short), INTENT (IN) :: method
REAL (KIND = float), OPTIONAL, INTENT (IN) :: power
INTEGER (KIND = short), OPTIONAL, INTENT (IN) :: near

!Arguments with intent (inout):
TYPE (grid_real), INTENT (INOUT) :: grid

!Local declarations
TYPE (ObservationalNetwork) :: activeNetwork
INTEGER (KIND = short) :: count
INTEGER (KIND = short) :: i, j, k, s, t
INTEGER (KIND = short) :: nearPoints
REAL (KIND = float)    :: actPower
REAL (KIND = float),POINTER   :: dist(:,:)  
REAL (KIND = float)   :: distIJ
REAL (KIND = float)   :: denom 
REAL (KIND = float)   :: weight 
!-----------------------end of declarations------------------------------------

!check for available measurements
CALL ActualObservations (network, count, activeNetwork)

!allocate distances vector
ALLOCATE (dist (activeNetwork % countObs + 1,2) )

!set points
point1 % system = grid % grid_mapping
point2 % system = grid % grid_mapping

!set near
IF (PRESENT (near)) THEN
  IF (activeNetwork % countObs <= near) THEN 
    nearPoints = activeNetwork % countObs
  ELSE
    nearPoints = near
  END IF
ELSE
  nearPoints = activeNetwork % countObs !use all data
END IF

!set power
IF (PRESENT (power)) THEN
  actPower = power
ELSE
  actPower = 2. !default to square
END IF

DO i = 1, grid % idim
  DO j = 1, grid % jdim
    IF (grid % mat (i,j) /= grid % nodata) THEN
      dist = -9999.99
      !compute distance from cell center to observations
      CALL GetXY (i,j,grid, point1 % easting, point1 % northing)
      DO k = 1, activeNetwork % countObs        
         point2 % northing = activeNetwork % obs (k) % xyz % northing  
         point2 % easting = activeNetwork % obs (k) % xyz % easting
         distIJ = Distance (point1,point2)
                  
		 !search for neighbours
		 DO s = 1, nearPoints
			IF ( dist(s,1) == -9999.99 .OR. &
			     dist(s,1) > distIJ       ) THEN
				DO t = nearPoints, s, -1
					dist(t+1,1) = dist(t,1)
					dist(t+1,2) = dist(t,2)
				END DO
				dist(s,1) = distIJ
				dist(s,2) = activeNetwork % obs(k) % value
				EXIT
			END IF
		 END DO
	  END DO	 
	  !compute denominator
	  denom = 0.0
	  IF (method == 1) THEN !Shepard's method
	      DO s = 1, nearPoints
	 	      IF (dist(s,1) <= 1.) THEN
			     !observation in cell, set minimum distance to avoid dividing by zero
			     dist(s,1) = 1.
		      END IF
		      denom = denom + dist(s,1) ** (-actPower)
	      END DO
	      ! weighted value
	      grid % mat(i,j) = 0
	      DO s = 1, nearPoints
	        weight = dist(s,1) ** (-actPower) / denom
	        grid % mat(i,j) = grid % mat(i,j) + weight * dist(s,2)
	      END DO
	   ELSE !Franke & Nielson method
	      DO s = 1, nearPoints
	 	      IF (dist(s,1) <= 1.) THEN
			     !observation in cell, set minimum distance to avoid dividing by zero
			     dist(s,1) = 1.
		      END IF
		      denom = denom + ( (dist(nearPoints,1) - dist(s,1)) / (dist(nearPoints,1) * dist(s,1)) ) ** actPower
	      END DO
	      ! weighted value
	      grid % mat(i,j) = 0
	      DO s = 1, nearPoints
	        weight = ( (dist(nearPoints,1) - dist(s,1)) / (dist(nearPoints,1) * dist(s,1)) ) ** actPower / denom
	        grid % mat(i,j) = grid % mat(i,j) + weight * dist(s,2)
	      END DO
	   END IF		 
    END IF
  END DO
END DO

DEALLOCATE(dist)
DEALLOCATE(activeNetwork % obs)

RETURN

END SUBROUTINE IDW



!==============================================================================
!| Description:
!   This subroutine implements the method used in the MICROMET program (see
!   reference). Wind speed is interpolated accounting for wind direction
!   and an empirical weigth that considers slope and curvature (topographic
!   effect)
!
! References:
!
!  Liston, G. E., Elder, K.,  2006. A Meteorological Distribution System 
!  for High-Resolution Terrestrial Modeling (MicroMet). 
!  Journal of Hudrometeorology, 7, 217-234. 
SUBROUTINE WindMicromet &
!
(speed, dir, slope, curvature, slope_az, slopewt, curvewt, grid, winddir)

USE Units, ONLY: &
!Imported parameters
degToRad, radToDeg, pi

IMPLICIT NONE

!Arguments with intent (in):
TYPE (ObservationalNetwork), INTENT(IN) :: speed !windspeed observations at stations 
TYPE (ObservationalNetwork), INTENT(IN) :: dir !wind direction observations at stations 
TYPE (grid_real), INTENT(IN) :: slope !terrain slope grid
TYPE (grid_real), INTENT(IN) :: curvature !terrain curvature grid
TYPE (grid_real), INTENT(IN) :: slope_az !terrain slope azimuth
REAL (KIND = float),  INTENT(IN) :: slopewt !slope weighting factor
REAL (KIND = float),  INTENT(IN) :: curvewt !curvature weighting factor

!Arguments with intent (inout):
TYPE (grid_real), INTENT (INOUT) :: grid !wind speed grid [m/s]
TYPE (grid_real), OPTIONAL, INTENT (INOUT) :: winddir !wind direction [deg]


!Local declarations
TYPE (ObservationalNetwork) :: temp_speed, temp_dir, active_speed, active_dir
TYPE (ObservationalNetwork) :: uwind, vwind
INTEGER :: count_speed, count_dir, i, j
TYPE (grid_real) :: uwind_grid !zonal wind [m/s]
TYPE (grid_real) :: vwind_grid !meridional wind [m/s]
TYPE (grid_real) :: winddir_grid !wind direction [rad]
TYPE (grid_real) :: wind_slope !slope in the direction of the wind [rad]
REAL (KIND = float) :: wslope_max ![rad]
REAL (KIND = float) :: windwt !wind weighting factor
REAL (KIND = float) :: thetad !diverting factor
REAL (KIND = float),  PARAMETER :: windspd_min = 0.00001 ![m/s]


!----------------end of declarations-------------------------------------------

!check for available measurements. Both speed and direction must exist
!first check nodata. If only speed or direction are missing, set both to missing 
! assume dir and speed sations are in memory in the same order
CALL CopyNetwork (speed,temp_speed)
CALL CopyNetwork (dir,temp_dir)

DO i = 1, temp_speed %countObs
  IF (temp_speed % obs (i) % value == temp_speed % nodata) THEN
      !set dir to nodata
      temp_dir % obs (i) % value = temp_dir % nodata
  ELSE IF (temp_dir % obs (i) % value == temp_dir % nodata) THEN
      !set speed to nodata
      temp_speed % obs (i) % value = temp_speed % nodata
  END IF
END DO

!now remove nodata
CALL ActualObservations (temp_speed, count_speed, active_speed)
CALL ActualObservations (temp_dir, count_dir, active_dir)

!initialize zonal and meridional component network
CALL CopyNetwork (active_speed, uwind)
CALL CopyNetwork (active_speed, vwind)

!initialize grid
CALL NewGrid (uwind_grid, grid)
CALL NewGrid (vwind_grid, grid)
CALL NewGrid (winddir_grid, grid)
CALL NewGrid (wind_slope, grid)

!compute u and v at stations
DO i = 1, active_speed % countObs
  uwind % obs (i) % value = - active_speed % obs (i) % value * &
                          SIN ( active_dir % obs (i) % value * degToRad)
  vwind % obs (i) % value = - active_speed % obs (i) % value * &
                          COS ( active_dir % obs (i) % value * degToRad)
END DO

!interpolate u and v grid 
CALL IDW (network=uwind, grid=uwind_grid, method=1, power=2.0, near=5)
!CALL BarnesOI (network=uwind, grid=uwind_grid, gammapar = 0.6)
     
CALL IDW (network=vwind, grid=vwind_grid, method=1, power=2.0, near=5)
!CALL BarnesOI (network=vwind, grid=vwind_grid, gammapar = 0.6)
   
!compute wind direction and windspeed grid 
DO i = 1, grid % idim
  DO j = 1, grid % jdim
      IF (grid % mat (i,j) /= grid % nodata) THEN
           
        ! Some compilers do not allow both u and v to be 0.0 in
        !   the atan2 computation.
        IF (ABS(uwind_grid % mat (i,j)) < 1e-10) &
              uwind_grid % mat (i,j) = 1e-10
           
        winddir_grid %mat (i,j) = &
            ATAN2(uwind_grid % mat (i,j),vwind_grid % mat (i,j))
  
        IF (winddir_grid %mat (i,j) >= pi) THEN
          winddir_grid % mat (i,j) = winddir_grid % mat (i,j) - pi
        ELSE
          winddir_grid % mat (i,j) = winddir_grid % mat (i,j) + pi
        END IF
           
        !windspeed
        grid % mat (i,j) = &
            SQRT(uwind_grid % mat (i,j)**2 + vwind_grid % mat (i,j)**2)
            
      END IF
  END DO
END DO

! Compute the slope in the direction of the wind.
DO i = 1, grid % idim
  DO j = 1, grid % jdim
      IF (grid % mat (i,j) /= grid % nodata) THEN
     
      wind_slope % mat (i,j) = slope % mat (i,j) * &
            COS ( winddir_grid % mat (i,j) - slope_az % mat (i,j) )
         
      END IF
  END DO
END DO

! Scale the wind slope such that the max abs(wind slope) has a value
! of abs(0.5).  Include a 1 mm slope in slope_max to prevent
! divisions by zero in flat terrain where the slope is zero.
wslope_max = 0.0 + 0.001
DO i = 1, grid % idim
  DO j = 1, grid % jdim
    IF (grid % mat (i,j) /= grid % nodata) THEN
        wslope_max = MAX (wslope_max,ABS(wind_slope % mat (i,j)))
    END IF
  END DO
END DO
      
DO i = 1, grid % idim
  DO j = 1, grid % jdim
    IF (grid % mat (i,j) /= grid % nodata) THEN
        wind_slope % mat (i,j) = wind_slope % mat (i,j) / (2.0 * wslope_max)
    END IF
  END DO
END DO

! Calculate the wind speed and direction adjustments.  The
! curvature and wind_slope values range between -0.5 and +0.5.
! Valid slopewt and curvewt values are between 0 and 1, with
! values of 0.5 giving approximately equal weight to slope and
! curvature.  I suggest that slopewt and curvewt be set such
! that slopewt + curvewt = 1.0.  This will limit the total
! wind weight to between 0.5 and 1.5 (but this is not required).
DO i = 1, grid % idim
  DO j = 1, grid % jdim
    IF (grid % mat (i,j) /= grid % nodata) THEN
            
      ! Compute the wind weighting factor.
      windwt = 1.0 + slopewt * wind_slope % mat (i,j) + &
      curvewt * curvature % mat (i,j)
      !if (windwt>0) write(*,*) windwt,  wind_slope % mat (i,j), curvature % mat (i,j)
      !pause
            
      ! Generate the terrain-modified wind speed.
      grid % mat (i,j) = windwt * grid % mat (i,j)
      
      !compute diverting factor
      thetad = - 0.5 * wind_slope % mat (i,j) * &
          SIN ( 2. * (slope_az % mat (i,j) - winddir_grid % mat (i,j)) )
      
      IF (PRESENT (winddir)) THEN
      
         winddir % mat (i,j) = ( winddir_grid % mat (i,j) + thetad ) * radToDeg
         
      END IF   
              
    END IF
  END DO
END DO

!deallocate memory
CALL DestroyNetwork (temp_speed)
CALL DestroyNetwork (temp_dir)
CALL DestroyNetwork (active_speed)
CALL DestroyNetwork (active_dir)
CALL DestroyNetwork (uwind)
CALL DestroyNetwork (vwind)

CALL GridDestroy (uwind_grid)
CALL GridDestroy (vwind_grid)
CALL GridDestroy (winddir_grid)
CALL GridDestroy (wind_slope)

RETURN
END SUBROUTINE WindMicromet


!==============================================================================
!| Description:
!   This subroutine implements the method presented by Gonzalez-Longatt et al.
!   (2015). Zonal and meridional components are computed and then orographic 
!   correction is applied. The two components are then re-composed to
!   provide final result.
!
! References:
!
!  González-Longatt, F., Medina, H., Serrano González, J., Spatial interpolation 
!     and orographic correction to estimate wind energy resource in Venezuela.
!     Renewable and Sustainable Energy Reviews, 48, 1-16, 2015.
!
SUBROUTINE WindGonzalezLongatt &
!
(speed, dir, dem, grid, winddir)

USE Units, ONLY: &
!Imported parameters
degToRad, radToDeg, pi

IMPLICIT NONE

!Arguments with intent (in):
TYPE (ObservationalNetwork), INTENT(IN) :: speed !windspeed observations at stations 
TYPE (ObservationalNetwork), INTENT(IN) :: dir !wind direction observations at stations 
TYPE (grid_real), INTENT(IN) :: dem !digital elevation model

!Arguments with intent (inout):
TYPE (grid_real), INTENT (INOUT) :: grid !wind speed grid [m/s]
TYPE (grid_real), OPTIONAL, INTENT (INOUT) :: winddir !wind direction [deg]


!Local declarations
TYPE (ObservationalNetwork) :: temp_speed, temp_dir, active_speed, active_dir
TYPE (ObservationalNetwork) :: uwind, vwind
INTEGER :: count_speed, count_dir, i, j
TYPE (grid_real) :: uwind_grid !zonal wind [m/s]
TYPE (grid_real) :: vwind_grid !meridional wind [m/s] !OK
REAL (KIND = float) :: vxcalc !zonal wind with orograpcic correction applied [m/s]
REAL (KIND = float) :: vycalc !meridional wind with orograpcic correction applied [m/s]
REAL (KIND = float) :: corr1, corr2, cellsize
LOGICAL :: validNorth, validSouth, validEast, validWest


!----------------end of declarations-------------------------------------------

!check for available measurements. Both speed and direction must exist
!first check nodata. If only speed or direction are missing, set both to missing 
! assume dir and speed stations are in memory in the same order
CALL CopyNetwork (speed,temp_speed)
CALL CopyNetwork (dir,temp_dir)

DO i = 1, temp_speed %countObs
  IF (temp_speed % obs (i) % value == temp_speed % nodata) THEN
      !set dir to nodata
      temp_dir % obs (i) % value = temp_dir % nodata
  ELSE IF (temp_dir % obs (i) % value == temp_dir % nodata) THEN
      !set speed to nodata
      temp_speed % obs (i) % value = temp_speed % nodata
  END IF
END DO

!now remove nodata
CALL ActualObservations (temp_speed, count_speed, active_speed)
CALL ActualObservations (temp_dir, count_dir, active_dir)

!initialize zonal and meridional component network
CALL CopyNetwork (active_speed, uwind)
CALL CopyNetwork (active_speed, vwind)

!initialize grid
CALL NewGrid (uwind_grid, grid)
CALL NewGrid (vwind_grid, grid)

!compute u and v at stations
DO i = 1, active_speed % countObs
  uwind % obs (i) % value = - active_speed % obs (i) % value * &
                          SIN ( active_dir % obs (i) % value * degToRad)
  vwind % obs (i) % value = - active_speed % obs (i) % value * &
                          COS ( active_dir % obs (i) % value * degToRad)
END DO

!interpolate u and v grid 
CALL IDW (network=uwind, grid=uwind_grid, method=1, power=2.0, near=5)
!CALL BarnesOI (network=uwind, grid=uwind_grid, gammapar = 0.6)
     
CALL IDW (network=vwind, grid=vwind_grid, method=1, power=2.0, near=5)
!CALL BarnesOI (network=vwind, grid=vwind_grid, gammapar = 0.6)


!apply orographic correction
DO i = 1, grid % idim
  DO j = 1, grid % jdim
    IF (grid % mat (i,j) /= grid % nodata) THEN

       !set cellsize
       cellsize = (CellArea (grid,i,j)  ) ** 0.5 ![m]
       
       !check for neighbour cells. Nodata and out
       !of grid cells must not be considered
       !for orographic correction     
       validNorth = CellIsValid (i-1, j, grid)
       validSouth = CellIsValid (i+1, j, grid)
       validEast  = CellIsValid (i, j+1, grid)
       validWest  = CellIsValid (i, j-1, grid)

       !correct zonal (along x) component
       IF (validEast .AND. validWest)  THEN !apply correction where east and west data are defined

          !west correction factor component
          corr1 = (dem % mat (i,j)  -  dem % mat (i,j-1)) * &
                (uwind_grid % mat (i,j-1) - uwind_grid % mat (i,j)  ) / cellsize
          
          !east correction factor component
          corr2 = (dem % mat (i,j+1)  -  dem % mat (i,j)) * &
                (uwind_grid % mat (i,j) - uwind_grid % mat (i,j+1)  ) / cellsize
          vxcalc = uwind_grid % mat(i,j) + 0.5 * ( corr1 + corr2)
       ELSE !do not apply correction
          vxcalc = uwind_grid % mat(i,j)
       END IF

       !correct meridional (along y) component
       IF (validNorth .AND. validSouth)  THEN  !apply correction north and south data are defined

          !south correction factor component
          corr1 = (dem % mat (i,j)  -  dem % mat (i+1,j)) * &
                (vwind_grid % mat (i+1,j) - vwind_grid % mat (i,j)  ) / cellsize
          
          !north correction factor component
          corr2 = (dem % mat (i-1,j)  -  dem % mat (i,j)) * &
                (vwind_grid % mat (i,j) - vwind_grid % mat (i-1,j)  ) / cellsize
          vycalc = vwind_grid % mat(i,j) + 0.5 * ( corr1 + corr2)

       ELSE !do not apply correction
          vycalc = vwind_grid % mat(i,j)
       END IF  
       
       !compute wind direction if required
       IF (PRESENT (winddir)) THEN
           ! Some compilers do not allow both u and v to be 0.0 in
           ! the atan2 computation.
           IF ( ABS(vxcalc ) < 1e-10) vxcalc = 1e-10
           
           winddir % mat (i,j) = ATAN2(vxcalc,vycalc)
  
           IF (winddir % mat (i,j) >= pi) THEN
              winddir % mat (i,j) = winddir % mat (i,j) - pi
           ELSE
              winddir % mat (i,j) = winddir % mat (i,j) + pi
           END IF
           !convert radians to deg
           winddir % mat (i,j) = winddir % mat (i,j) * radToDeg
       ENDIF
           
        !windspeed
        grid % mat (i,j) = SQRT(vxcalc**2. + vycalc**2.)
        
    END IF
  END DO
END DO


!deallocate memory
CALL DestroyNetwork (temp_speed)
CALL DestroyNetwork (temp_dir)
CALL DestroyNetwork (active_speed)
CALL DestroyNetwork (active_dir)
CALL DestroyNetwork (uwind)
CALL DestroyNetwork (vwind)

CALL GridDestroy (uwind_grid)
CALL GridDestroy (vwind_grid)

RETURN
END SUBROUTINE WindGonzalezLongatt




!==============================================================================
!| Description:
!   The nearest neighbor algorithm selects the value of the nearest point 
!   and does not consider the values of neighboring points at all, 
!   yielding a piecewise-constant interpolant
!
! References:
!
!  A.H. Thiessen. 1911. Precipitation averages for large areas. 
!  Monthly Weather Review, 39(7): 1082-1084. 
SUBROUTINE NearestNeighbor &
!
(network, grid, weights, gridPolygons)

IMPLICIT NONE

!Arguments with intent (in):
TYPE (ObservationalNetwork), INTENT(IN) :: network

!Arguments with intent (inout):
TYPE (grid_real), INTENT (INOUT) :: grid

!Arguments with intent (out):
REAL, OPTIONAL, INTENT(OUT), POINTER      :: weights(:)
TYPE (grid_integer), OPTIONAL, INTENT(OUT) :: gridPolygons

!Local declarations
TYPE (ObservationalNetwork) :: activeNetwork
INTEGER (KIND = short) :: count
INTEGER(KIND = short), ALLOCATABLE  :: polygons(:,:)
REAL (KIND = float) :: dist, dist_min
INTEGER (KIND = short) :: i, j, k

!----------------end of declarations-------------------------------------------

!check for available measurements
CALL ActualObservations (network, count, activeNetwork)

!set points
point1 % system = grid % grid_mapping
point2 % system = grid % grid_mapping

!allocate polygons matrix
ALLOCATE (polygons (grid % idim, grid % jdim) )
polygons = -1


DO i = 1, grid % idim
	col:DO j = 1, grid % jdim
		IF (grid % mat(i,j) == grid % nodata) THEN
			CYCLE COL
		ELSE
			!initialize
			grid % mat(i,j) = 0.0
		END IF
		dist = -9999.99
		dist_min = -9999.99
		
		!compute distance from cell center to observations
		CALL GetXY (i,j,grid,point1 % easting,point1 % northing)
        DO k = 1, activeNetwork % countObs 
           point2 % northing = activeNetwork % obs (k) % xyz % northing  
           point2 % easting = activeNetwork % obs (k) % xyz % easting
           dist = Distance(point1,point2)
		   !search for nearest points
			IF ( dist_min == -9999.99 .OR. dist_min > dist ) THEN
					polygons(i,j) = k
				    dist_min = dist
			END IF
		END DO
	END DO col
END DO

!finalize interpolation
DO i = 1, grid % idim
	DO j = 1, grid % jdim
		IF (grid % mat(i,j) == grid % nodata) CYCLE
		k = polygons (i,j)
		grid % mat(i,j) = activeNetwork % obs (k) % value
	END DO
END DO

IF (PRESENT (gridPolygons)) THEN
    CALL NewGrid (gridPolygons,grid)
	gridPolygons % mat = polygons
END IF

IF (PRESENT(weights)) THEN
    ALLOCATE(weights(activeNetwork % countObs))
    weights = 0.
	    DO i = 1, grid % idim
		    DO j = 1, grid % jdim
			    IF (grid % mat(i,j) == grid % nodata) CYCLE
			    weights(polygons(i,j)) = weights(polygons(i,j)) + 1
		    END DO
	    END DO
    weights = weights / CountCells(grid)
END IF

RETURN
END SUBROUTINE NearestNeighbor



!==============================================================================
!| Description:
!   The method constructs a grid of size determined by the distribution 
!   of the two dimensional data points. Using this grid, the function 
!   values are calculated at each grid point. To do this the method 
!   utilises a series of Gaussian functions, given a distance weighting 
!   in order to determine the relative importance of any given measurement 
!   on the determination of the function values. Correction passes are 
!   then made to optimise the function values, by accounting for the 
!   spectral response of the interpolated points.
!
! References:
!
!  Barnes, S. L., 1964: A technique for maximizing details in numerical 
!     map analysis. Journal of Applied Meteorology, 3, 395–409.
!
!  Koch, S., M. desJardins,and P. Kocin, 1983: An Interactive Barnes 
!    Objective Map Analysis Scheme for Use with Satellite and 
!    Convectional Data. Journal of Appl. Meteor., 22, 1487-1503
!
!  Maddox, R., 1980 : An objective technique for seaprating 
!    macroscale and mesoscale features in meteorological data. 
!    Mon. Wea. Rev., 108, 1108-1121
!
SUBROUTINE BarnesOI &
!
(network, grid, gammapar)

USE Units, ONLY: &
!Imported parameters
pi

IMPLICIT NONE

!Arguments with intent (in):
TYPE (ObservationalNetwork), INTENT(IN) :: network

REAL (KIND = float), OPTIONAL, INTENT(IN) :: gammapar

!Arguments with intent (inout):
TYPE (grid_real), INTENT (INOUT) :: grid

!local declarations:
REAL (KIND = float) :: dn         !!data spacing used in the analysis
REAL (KIND = float) :: gamma      !! smoothing parameter [0.2 - 1.0]
REAL (KIND = float) :: xkappa_1   !! Gauss constant for first pass
REAL (KIND = float) :: xkappa_2   !! Gauss constant for second pass
REAL (KIND = float) :: rmax_1     !! maximum scanning radii, for first
REAL (KIND = float) :: rmax_2     !! and second passes
REAL (KIND = float) :: anum_1     !! numerator, beyond scanning radius,
REAL (KIND = float) :: anum_2     !! for first and second passes
REAL (KIND = float) :: xa,ya      !! x, y coords of current station
REAL (KIND = float) :: xb,yb      !! x, y coords of current station
REAL (KIND = float) :: w1,w2      !! weights for Gauss-weighted average
REAL (KIND = float) :: wtot1,wtot2 !!sum of weights
REAL (KIND = float) :: ftot1,ftot2 !!accumulators for values, corrections
REAL (KIND = float) :: dsq !!square of distance between two points
REAL (KIND = float) :: dnMax, dnMin
REAL (KIND = float) :: nc, nr !! number of columns and rows in the grid
REAL (KIND = float) :: cellsize ![m]
REAL (KIND = float), ALLOCATABLE :: dvar (:) !!estimated error
TYPE (ObservationalNetwork) :: activeNetwork
INTEGER (KIND = short) :: count
INTEGER (KIND = short) :: i, j, k !! loop indexes

!----------------end of declarations-------------------------------------------

!check for available measurements
CALL ActualObservations (network, count, activeNetwork)

!Allocate dvar
ALLOCATE (dvar(activeNetwork % countObs))

!set points
point1 % system = grid % grid_mapping
point2 % system = grid % grid_mapping

!set gamma to default or user specified value
IF (PRESENT(gammapar)) THEN
  gamma = gammapar
ELSE
  gamma = 0.2 !default value
END IF

!get dn
!compute average cellsize.
cellsize = ( CellArea (grid, grid % idim / 2, grid % jdim / 2) ) ** 0.5 ![m]
nc = grid % jdim
nr = grid % idim
dnMax = ( (nr * cellsize) * (nc * cellsize) ) ** 0.5 * &
        ((1.0 + activeNetwork % countObs ** 0.5) / (activeNetwork % countObs - 1.0))

dnMin = ( (nr * cellsize) * (nc * cellsize) / activeNetwork % countObs ) ** 0.5

dn = 0.5 * (dnMin + dnMax)
!debug
dn = 6. * cellsize

! Compute the first and second pass values of the scaling parameter
! and the maximum scanning radius used by the Barnes scheme.
! Values above this maximum will use a 1/r**2 weighting.

! First-round values
	xkappa_1 = 5.052 * (2. * dn / Pi) ** 2.0

! Define the maximum scanning radius to have weight defined by
! wt = 1.0 x 10**(-30) = exp(-rmax_1/xkappa_1)
! Also scale the 1/r**2 wts so that when dsq = rmax, the wts match.
	rmax_1 = xkappa_1 * 30.0 * log(10.0)
	anum_1 = 1.E-30 * rmax_1
	
! Second-round values
	xkappa_2 = gamma * xkappa_1
	rmax_2 = rmax_1 * gamma
	anum_2 = 1.E-30 * rmax_2
	
! Scan each input data point and construct estimated error, dvar, at that point
outer_loop: DO i = 1, activeNetwork % countObs

        point1 % northing = activeNetwork % obs (i) % xyz % northing  
        point1 % easting  = activeNetwork % obs (i) % xyz % easting
		wtot1 = 0.0
		ftot1 = 0.0
		
   inner_loop: DO j = 1, activeNetwork % countObs
   
            point2 % northing = activeNetwork % obs (j) % xyz % northing  
            point2 % easting  = activeNetwork % obs (j) % xyz % easting
            dsq = Distance (point1,point2) ** 2.
            
            IF (dsq <= rmax_1) THEN
               w1 = exp((- dsq)/xkappa_1)
            ELSE !Assume a 1/r**2 weight
               w1 = anum_1/dsq
            END IF

			wtot1 = wtot1 + w1
			ftot1 = ftot1 + w1 * activeNetwork % obs (j) % value
    END DO inner_loop

!
!		if(wtot1==0.0) printf("stn wt totals zero\n");

        dvar(i) = activeNetwork % obs (i) % value - ftot1/wtot1

END DO outer_loop

! Grid-prediction loop.  Generate the estimate using first set of
! weights, and correct using error estimates, dvar, and second
! set of weights.

row_loop: DO i = 1, grid % idim
  col_loop: DO j = 1, grid % jdim
  
  IF (grid % mat (i,j) /= grid % nodata) THEN
      CALL GetXY (i,j,grid, point1 % easting, point1 % northing)

      ! Scan each input data point.
      ftot1 = 0.0
      wtot1 = 0.0
      ftot2 = 0.0
      wtot2 = 0.0
      
      stations_loop: DO k = 1, activeNetwork % countObs
          point2 % northing = activeNetwork % obs (k) % xyz % northing  
          point2 % easting  = activeNetwork % obs (k) % xyz % easting
          
          dsq = Distance (point1,point2) ** 2.

          IF (dsq<=rmax_2) THEN
              w1 = exp((- dsq)/xkappa_1)
              w2 = exp((- dsq)/xkappa_2)
          ELSE IF (dsq<=rmax_1) THEN
              w1 = exp((- dsq)/xkappa_1)
              w2 = anum_2/dsq;
          ELSE  
              !Assume a 1/r**2 weight.
              w1 = anum_1/dsq
              !With anum_2/dsq.
              w2 = gamma * w1
          END IF

          wtot1 = wtot1 + w1
          wtot2 = wtot2 + w2
          ftot1 = ftot1 + w1 * activeNetwork % obs (k) % value
          ftot2 = ftot2 + w2 * dvar (k)
 
      END DO stations_loop
!
!			if (wtot1==0.0 || wtot2==0.0) printf("wts total zero\n");
!
			grid % mat (i,j) =  ftot1/wtot1 + ftot2/wtot2

  END IF
  END DO col_loop
END DO row_loop



DEALLOCATE(activeNetwork % obs)
DEALLOCATE (dvar)


RETURN
END SUBROUTINE BarnesOI


!============================================================
!| Description:
! Inverse matrix
! Method: Based on Doolittle LU factorization for Ax=b
!-----------------------------------------------------------
! input:
! gamma(nest,nest) - array of covariance coefficients for matrix gamma
! nest      - dimension
! outpu:
! inv_gamma(nest,nest) - inverse matrix of A
! 
! IMPORTANT: the original matrix gamma(nest,nest) is destroyed 
! during the calculation. Define A for avoiding this.
!
 SUBROUTINE inverse(gamma,nest,inv_gamma)

  IMPLICIT NONE 
! Calling variables  
  INTEGER, INTENT(IN) :: nest
  DOUBLE PRECISION, DIMENSION(nest,nest), INTENT(IN) :: gamma 
  DOUBLE PRECISION, DIMENSION(nest,nest), INTENT(OUT) :: inv_gamma
! Local variables  
  DOUBLE PRECISION, DIMENSION(nest,nest)  :: A,L,U
  DOUBLE PRECISION, DIMENSION(nest)  ::  b,d,x
  DOUBLE PRECISION ::  coeff
  INTEGER :: i, j, k
  
! step 0: initialization for matrices 
  A=gamma
  inv_gamma=0.
  L=0.
  U=0.
  b=0.
  d=0.
  x=0.
  coeff=0.
  

! step 1: forward elimination
  DO k=1, nest-1
     DO i=k+1,nest
        coeff=A(i,k)/A(k,k)
        
        L(i,k) = coeff
        DO j=k+1,nest
           A(i,j) = A(i,j)-coeff*A(k,j)
        ENDDO
     ENDDO
  ENDDO
  

! Step 2: prepare L and U matrices 
! L matrix is A matrix of the elimination coefficient
! + the diagonal elements are 1.0
  DO i=1,nest
     L(i,i) = 1.
  ENDDO
! U matrix is the upper triangular part of A
  DO j=1,nest
     DO i=1,j
        U(i,j) = A(i,j)
     ENDDO
  ENDDO

! Step 3: compute columns of the inverse matrix C
  DO k=1,nest
     b(k)=1.0
     d(1) = b(1)
! Step 3a: Solve Ld=b using the forward substitution
     DO i=2,nest
        d(i)=b(i)
        DO j=1,i-1
           d(i) = d(i) - L(i,j)*d(j)
        ENDDO
     ENDDO
! Step 3b: Solve Ux=d using the back substitution
     x(nest)=d(nest)/U(nest,nest)
     DO i = nest-1,1,-1
        x(i) = d(i)
        DO j=nest,i+1,-1
           x(i)=x(i)-U(i,j)*x(j)
        ENDDO
        x(i) = x(i)/u(i,i)
     ENDDO
! Step 3c: fill the solutions x(nest) into column k of C
     DO i=1,nest
        inv_gamma(i,k) = x(i)
     ENDDO
     b(k)=0.0
  ENDDO
 END SUBROUTINE inverse

 
!==============================================================================
!| Description:
! Sort distances in increasing order
!
!    Sort an array and make the same interchanges in
!            an auxiliary array.  
!
!   Description of Parameters
!      distance - array of values to be sorted   
!      dist_sort - array to be carried with distance (all swaps of distance elements are
!          matched in dist_sort .  After the sort ind_vec contains the original
!         position of the value i in the unsorted distance array and ind_vec_sort the
!         initial indexes once sorted
!      nest - number of stations to be sorted
! 
  SUBROUTINE Sort (distance,ind_vec,nest,dist_sort,ind_vec_sort) 

  IMPLICIT NONE


! Calling variables  
  INTEGER, INTENT(IN) :: nest
  DOUBLE PRECISION, DIMENSION(nest), INTENT(IN) :: distance 
  DOUBLE PRECISION, DIMENSION(nest), INTENT(OUT) :: dist_sort
  INTEGER, DIMENSION(nest), INTENT(IN) :: ind_vec    
  INTEGER, DIMENSION(nest), INTENT(OUT) :: ind_vec_sort  
! Local variables  
  INTEGER :: i, iswap, itemp, iswap1
  DOUBLE PRECISION, DIMENSION(nest)  ::  temp
  
!
   dist_sort=distance
   ind_vec_sort=ind_vec
      
!    MINLOC is a FORTRAN 90 function that returns the index value for the
!    minimum element in the array
   DO  i=1,nest-1

       iswap=MINLOC(dist_sort(i:nest),dim=1)
       
       iswap1=iswap+i-1
       
       IF(iswap1 .NE. i) THEN
          temp(i)=dist_sort(i)
          dist_sort(i)=dist_sort(iswap1)
          dist_sort(iswap1)=temp(i)
          
          itemp=ind_vec_sort(i)
          ind_vec_sort(i)=ind_vec_sort(iswap1)
          ind_vec_sort(iswap1)=itemp
         ENDIF
    ENDDO

  END SUBROUTINE SORT
  
 









END MODULE SpatialInterpolation